package com.yeziji.utils.expansion;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.service.IService;
import com.mybatisflex.core.util.LambdaGetter;
import com.yeziji.common.CommonEntity;
import com.yeziji.common.CommonErrorEnum;
import com.yeziji.common.CommonErrorMsg;
import com.yeziji.exception.ApiException;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 断言工具
 *
 * @author hwy
 * @since 2023/09/01 18:28
 **/
public class Asserts {
    /**
     * 主动抛出异常
     *
     * @param errorEnum 异常信息
     */
    public static <T> T error(CommonErrorEnum errorEnum) {
        throw new ApiException(errorEnum);
    }

    // --------- assert is judge

    /**
     * 布尔值判断
     *
     * @param judgeData 判断的数据
     * @param isTruth   将要异常的结果
     * @param errorEnum 异常信息
     * @return {@link Boolean} 如果不抛出异常就返回判断结果
     */
    public static boolean judge(Boolean judgeData, boolean isTruth, CommonErrorEnum errorEnum) {
        if (judgeData == null || judgeData.equals(isTruth)) {
            throw new ApiException(errorEnum);
        }
        return judgeData;
    }

    /**
     * 是否为 false
     *
     * @param judgeData 判断的数据
     * @param errorEnum 异常信息
     * @return {@link Boolean} 如果不抛出异常就返回判断结果
     */
    public static boolean isFalse(Boolean judgeData, CommonErrorEnum errorEnum) {
        return judge(judgeData, false, errorEnum);
    }

    /**
     * 是否为 true
     *
     * @param judgeData 判断的数据
     * @param errorEnum 异常信息
     * @return {@link Boolean} 如果不抛出异常就返回判断结果
     */
    public static boolean isTruth(Boolean judgeData, CommonErrorEnum errorEnum) {
        return !judge(judgeData, true, errorEnum);
    }


    // --------- assert is equals

    /**
     * 不相同就抛出异常
     *
     * @param obj1      对比数据1
     * @param obj2      对比数据2
     * @param errorEnum 异常枚举
     */
    public static boolean equals(Object obj1, Object obj2, CommonErrorEnum errorEnum) {
        if (!Objects.equals(obj1, obj2)) {
            throw new ApiException(errorEnum);
        }
        return true;
    }

    // --------- assert is not equals

    /**
     * 相同就抛出异常
     *
     * @param obj1      对比数据1
     * @param obj2      对比数据2
     * @param errorEnum 异常枚举
     */
    public static boolean notEquals(Object obj1, Object obj2, CommonErrorEnum errorEnum) {
        if (Objects.equals(obj1, obj2)) {
            throw new ApiException(errorEnum);
        }
        return true;
    }

    // ---------- assert is not null

    /**
     * 数据 data 如果为空就抛出异常
     *
     * @param data      判断数据
     * @param errorEnum 异常信息
     * @param <T>       传入的泛型
     * @return {@link T} 返回传入的数据
     */
    public static <T> T notNull(T data, CommonErrorEnum errorEnum) {
        if (data == null) {
            throw new ApiException(errorEnum);
        }
        return data;
    }

    /**
     * 数据 data 如果为空就抛出异常
     *
     * @param data      判断数据
     * @param errorEnum 异常信息
     * @param consumer  非空执行
     */
    public static <T> void notNullThen(T data, CommonErrorEnum errorEnum, Consumer<T> consumer) {
        if (data == null) {
            throw new ApiException(errorEnum);
        } else if (consumer != null) {
            consumer.accept(data);
        }
    }

    @SafeVarargs
    public static <T, D> T notNullMappings(T data, CommonErrorEnum errorEnum, Function<T, D>... functions) {
        if (functions != null) {
            T td = notNull(data, errorEnum);
            Arrays.stream(functions).forEach(function -> {
                notNull(function.apply(td), errorEnum);
            });
            return td;
        }

        return notNull(data, errorEnum);
    }

    public static <T, D> D notNullMapping(T data, CommonErrorEnum errorEnum, Function<T, D> function) {
        if (function != null) {
            T td = notNull(data, errorEnum);
            return notNull(function.apply(td), errorEnum);
        }

        throw new ApiException(CommonErrorMsg.REQUIRED_DATA_IS_NULL);
    }

    @SafeVarargs
    public static <T> T notNullNestMapping(T data,
                                           CommonErrorEnum errorEnum,
                                           Function<T, Object> function,
                                           Function<T, Object>... functions) {
        List<Function<T, Object>> functionList = new ArrayList<>();
        if (functions.length > 0) {
            functionList.addAll(List.of(functions));
        }
        if (function != null) {
            functionList.add(function);
        }
        if (functionList.isEmpty()) {
            return data;
        } else {
            for (Function<T, Object> convertFunction : functionList) {
                notNull(convertFunction.apply(data), errorEnum);
            }
        }
        return data;
    }

    public static <T> T notNullNestMapping(T data, Map<Function<T, Object>, CommonErrorEnum> functionErrorMap) {
        if (MapUtil.isEmpty(functionErrorMap)) {
            return data;
        } else {
            for (Map.Entry<Function<T, Object>, CommonErrorEnum> entry : functionErrorMap.entrySet()) {
                Function<T, Object> convertFunction = entry.getKey();
                CommonErrorEnum errorEnum = entry.getValue();
                notNull(convertFunction.apply(data), errorEnum);
            }
        }
        return data;
    }

    // ---------- assert is not blank

    /**
     * 字符串为空就抛出异常
     *
     * @param data      判断数据
     * @param errorEnum 异常信息
     */
    public static String notBlank(String data, CommonErrorEnum errorEnum) {
        if (StrUtil.isBlank(data)) {
            throw new ApiException(errorEnum);
        }
        return data;
    }

    // ---------- assert is is null

    /**
     * 数据应该为空, 不为空就抛出异常
     *
     * @param data      数据
     * @param errorEnum 异常信息
     * @param <T>       数据泛型
     */
    public static <T> void isNull(T data, CommonErrorEnum errorEnum) {
        if (data != null) {
            throw new ApiException(errorEnum);
        }
    }


    // ---------- assert is required data
    public static <T> T requiredData(T data) {
        return notNull(data, CommonErrorMsg.REQUIRED_DATA_IS_NULL);
    }

    @SafeVarargs
    public static <T, D> T requiredData(T data, Function<T, D>... functions) {
        if (functions != null) {
            T td = requiredData(data);
            Arrays.stream(functions).forEach(function -> {
                requiredData(function.apply(td));
            });
            return td;
        }
        return requiredData(data);
    }

    // ---------- assert collection
    public static <T, O extends Collection<T>> O isNotEmpty(O list, CommonErrorEnum errorEnum) {
        if (Lists2.isEmpty(list)) {
            return error(errorEnum);
        }
        return list;
    }

    // ---------- assert is gt ( > )
    public static <T> T gt(T number, long contrastNumber, CommonErrorEnum errorEnum) {
        T td = requiredData(number);
        String integer = String.valueOf(td);
        if (!NumberUtil.isNumber(String.valueOf(td)) || Long.valueOf(integer).compareTo(contrastNumber) < 0) {
            return error(errorEnum);
        }
        return number;
    }

    public static long gt(long number, long contrastNumber, CommonErrorEnum errorEnum) {
        if (number < contrastNumber) {
            return error(errorEnum);
        }
        return number;
    }

    public static int gt(int number, int contrastNumber, CommonErrorEnum errorEnum) {
        if (number < contrastNumber) {
            return error(errorEnum);
        }
        return number;
    }

    // ---------- assert is lt ( < )
    public static <T> T lt(T number, long contrastNumber, CommonErrorEnum errorEnum) {
        T td = requiredData(number);
        String integer = String.valueOf(td);
        if (!NumberUtil.isNumber(String.valueOf(td)) || Long.valueOf(integer).compareTo(contrastNumber) > 0) {
            return error(errorEnum);
        }
        return number;
    }

    public static long lt(long number, long contrastNumber, CommonErrorEnum errorEnum) {
        if (number > contrastNumber) {
            return error(errorEnum);
        }
        return number;
    }

    public static int lt(int number, int contrastNumber, CommonErrorEnum errorEnum) {
        if (number > contrastNumber) {
            return error(errorEnum);
        }
        return number;
    }

    // ---------- assert is service exists
    public static <T extends CommonEntity> ExistsValidate<T> noExists(IService<T> iService) {
        return ExistsValidate.start(iService);
    }

    /**
     * 查询校验
     *
     * @param <T> 指定实体类泛型
     */
    public static class ExistsValidate<T extends CommonEntity> {
        private IService<T> iService;

        private final Map<LambdaGetter<T>, Object> eqGetters = new ConcurrentHashMap<>();

        private final Map<LambdaGetter<T>, Object> neGetters = new ConcurrentHashMap<>();

        private final Map<LambdaGetter<T>, Collection<?>> inGetters = new ConcurrentHashMap<>();

        private final Map<LambdaGetter<T>, Collection<?>> notInGetters = new ConcurrentHashMap<>();

        // No-parameter construction is prohibited
        private ExistsValidate() {
        }

        public static <T extends CommonEntity> ExistsValidate<T> start(IService<T> iService) {
            ExistsValidate<T> queryCheck = new ExistsValidate<>();
            queryCheck.iService = iService;
            return queryCheck;
        }

        public ExistsValidate<T> eq(LambdaGetter<T> eqGetter, Object data, Boolean... conditions) {
            if (Arrays.stream(conditions).allMatch(condition -> Objects.equals(condition, true))) {
                this.eqGetters.put(eqGetter, data);
            }
            return this;
        }

        public ExistsValidate<T> in(LambdaGetter<T> inGetter, Collection<?> list, Boolean... conditions) {
            if (Arrays.stream(conditions).allMatch(condition -> Objects.equals(condition, true))) {
                this.inGetters.put(inGetter, list);
            }
            return this;
        }

        public ExistsValidate<T> ne(LambdaGetter<T> neGetter, Object data, Boolean... conditions) {
            if (Arrays.stream(conditions).allMatch(condition -> Objects.equals(condition, true))) {
                this.neGetters.put(neGetter, data);
            }
            return this;
        }

        public ExistsValidate<T> notIn(LambdaGetter<T> notInGetter, Collection<?> list, Boolean... conditions) {
            if (Arrays.stream(conditions).allMatch(condition -> Objects.equals(condition, true))) {
                this.notInGetters.put(notInGetter, list);
            }
            return this;
        }

        /**
         * 校验通过后的自定义行为, 如果校验不通过则抛出异常
         *
         * @param commonErrorMsg 校验不通过时抛出的异常
         * @param doSomething    操作行为
         * @param <O>            结果泛型
         * @return {@link O} 需要的结果
         */
        public <O> O throwThen(CommonErrorEnum commonErrorMsg, @Nonnull Supplier<O> doSomething) {
            if (this.end(commonErrorMsg)) {
                return doSomething.get();
            }
            return null;
        }

        /**
         * 校验通过后的自定义行为, 如果校验不通过则抛出异常
         *
         * @param commonErrorMsg 校验不通过时抛出的异常
         * @param doSomething    操作行为
         */
        public void throwThen(CommonErrorEnum commonErrorMsg, @Nonnull Runnable doSomething) {
            if (this.end(commonErrorMsg)) {
                doSomething.run();
            }
        }

        /**
         * 如果校验不通过则抛出异常
         *
         * @param commonErrorMsg 抛出的异常
         */
        public void exception(CommonErrorEnum commonErrorMsg) {
            this.end(commonErrorMsg);
        }

        /**
         * 校验通过后的自定义行为, 如果校验不通过则正常返回
         *
         * @param doSomething 操作行为
         * @param <O>         结果泛型
         * @return {@link O} 需要的结果
         */
        public <O> O then(@Nonnull Supplier<O> doSomething) {
            if (this.end(null)) {
                return doSomething.get();
            }
            return null;
        }

        /**
         * 调用 exists 执行校验
         *
         * @return {@link Boolean} 校验结果
         */
        private boolean end(CommonErrorEnum commonErrorMsg) {
            // 全空就不校验
            if (MapUtil.isEmpty(this.eqGetters) &&
                    MapUtil.isEmpty(this.neGetters) &&
                    MapUtil.isEmpty(this.inGetters) &&
                    MapUtil.isEmpty(this.notInGetters)) {
                return true;
            }

            QueryWrapper queryWrapper = QueryWrapper.create();
            // eq
            for (Map.Entry<LambdaGetter<T>, Object> entry : this.eqGetters.entrySet()) {
                queryWrapper.where(entry.getKey()).eq(entry.getValue());
            }
            // ne
            for (Map.Entry<LambdaGetter<T>, Object> entry : this.neGetters.entrySet()) {
                queryWrapper.where(entry.getKey()).ne(entry.getValue());
            }
            // in
            for (Map.Entry<LambdaGetter<T>, Collection<?>> entry : this.inGetters.entrySet()) {
                queryWrapper.where(entry.getKey()).in(entry.getValue());
            }
            // not in
            for (Map.Entry<LambdaGetter<T>, Collection<?>> entry : this.notInGetters.entrySet()) {
                queryWrapper.where(entry.getKey()).notIn(entry.getValue());
            }

            // 校验结果
            boolean exists = iService.exists(queryWrapper);
            if (exists && commonErrorMsg != null) {
                return Asserts.isTruth(true, commonErrorMsg);
            }

            // 不存在返回 true, 说明校验通过
            return !exists;
        }
    }
}
